/***************************************************************************
 *   Copyright (C) 2005 by xihe   *
 *   xihels@163.com                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef _QQ_CLIENT_H_
#define _QQ_CLIENT_H_


#include "QQUser.h"
#include "Connection.h"
#include "crypt.h"

#include "XPThread.h"
#include "QQPacketBuf.h"
#include <stdint.h>
#include <vector>
#include <memory>
#include <stdint.h>
using std::vector;
using std::auto_ptr;
#include "Packet.h"



class LoginPacket;
class LoginReplyPacket;
class LoginTokenReplyPacket;
class ClusterOperateReply;
class UnknowPacket;

typedef InPacket* (*CREATE_FUN)(const uint8_t* buf, int len);

typedef auto_ptr<OutPacket> OutPacketPtr;
typedef auto_ptr<InPacket>  InPacketPtr;

/******************************************************/
void showbuf(const uint8_t* buf, int len) ;
/******************************************************/

class QQClient {
    public:
// 自身状态(用在KeepAlive函数中)
// 正常
// 网络断开
	enum { STATUS_OK, STATUS_DISCONNECT, STATUS_ERROR};
// 当前的网络状态,
// 正在登录
// 已经登录
// 离线
	enum {LOGIN_STATUS, ONLINE_STATUS, OFFINE_STATUS};

	friend class LoginTokenReplyPacket;
	friend class LoginReplyPacket;
	friend class ClusterOperateReply;
	friend class RegisterInPacket;
	friend class UnknowPacket;

	const uint8_t*	getPwkey() const	{ return user.getPwkey(); }
	uint32_t	getUid() const		{ return user.getUid(); }
	uint8_t		getLoginMode() const	{ return user.getLoginMode(); }
	bool		isTcp() const		{ return user.isTcp(); }
	bool		isFakeCam() const	{ return user.isFakeCam(); }
	uint16_t	getFace() const		{ return face; }
    protected:
	QQClient(const QQUser& user_);
	virtual ~QQClient() = 0;
	void    setFace(uint16_t _face) { face = _face; }
	void	netStatus(int status)	{ NETSTATUS = status; }
    public:
	const uint8_t*  getSessionKey() const { return sessionKey; }
	const uint8_t*  getFileSessionKey() const { return fileSessionKey; }
	const string&	getLoginToken() const { return loginToken; }
	const uint8_t*  getIniKey()	const { return iniKey; }

	uint16_t	stepSequence()	    { return sequence++; }
    public:
	void	sendPacket(OutPacketPtr packPtr);   // 发送包函数
	void	againSendQueueRemove(uint16_t sequence);
	void	login();			    // 登入
	void	logout();			    // 登出
	// 心跳函数(4秒调用它一次) 
	int	keepAlive();
	int	tryLogin();
	int	netStatus() const { return NETSTATUS; }
    public:
	static bool decrypt(const uint8_t* in, int len, 
		uint8_t* out, int& outlen,
		const uint8_t* key ) 
	{
	    outlen = len;
	    return (0 != qq_crypt(DECRYPT, (uint8_t*)in, len, (uint8_t*)key, out, &outlen));
	}

	static bool encrypt(const uint8_t* in, int len,
		uint8_t* out, int& outlen,
		const uint8_t* key ) 
	{
	    outlen = len + 16;
	    return (0 != qq_crypt(ENCRYPT, (uint8_t*)in, len, (uint8_t*)key, out, &outlen));
	}

    private:

	// 取得登录令牌
	void onLoginToken(const LoginTokenReplyPacket& packet);

	// 处理登录回复包
	void onLogin(const LoginReplyPacket& packet);

	// 处理未知包
	void onUnknow(const UnknowPacket&);

	// 接收线程函数
	void* recvPacket(void *);


	virtual void doRecvPacket(InPacket*) = 0;
	virtual void doKeepAlive(int status) = 0;
	virtual void doUnknow(const UnknowPacket& ) = 0;
	virtual void doLoginOk() = 0;
	virtual void doLoginError(const LoginReplyPacket& ) = 0;

	void  sendLoginPacket(const LoginPacket& packet); // 专门用来发送LoginPacket
	void  sendPacket(const OutPacket& packet);


	void doMd5(uint8_t* in, int len, uint8_t* out);

    private:
	void againSendQueuePush(OutPacket* packet);
	struct CompareSequence : public unary_function<uint16_t, bool> {
	    explicit CompareSequence(uint16_t _sequence) :
		sequence(_sequence)
	    {}
	    bool operator ()(Packet* packet) 
	    {
		return ((NULL != packet) && (packet->getSequence() == sequence)); 
	    }
	    uint16_t sequence;
	};

    private:
	typedef vector<OutPacket*> AGAIN_SEND_QUEUE;	// 重发队列
	
	volatile int	   RUNNING;			// 线程结束标志(为0时线程退出);
	int		   NETSTATUS;			// 当前的网络状态;
	
	// 检测是否在线的计数器
	volatile int	   connecting;		
	// 心跳检测时间,60秒后重置)
	int keepAliveTime;
	// 登录检测的次数
	int tryLoginCount;

	XPThread<QQClient>	recvThread;		// 接收线程

	uint8_t	    sendEncryptBuf[MAX_PACKET_SIZE];	// 发送加密缓冲区
	uint8_t	    sendBuf[MAX_PACKET_SIZE];		// 发送缓冲区

	AGAIN_SEND_QUEUE	againSendQueue;	// 重发队列


	QQUser	    user;			// QQ账号
	uint16_t    face;			// 头像
	Connection  connect;			// socket连接

	uint16_t    sequence;			// 当前包序号

	uint8_t  sessionKey[QQ_KEY_LENGTH];	// 会话密钥
	static uint8_t iniKey[QQ_KEY_LENGTH];	// 初始密钥
	uint8_t  fileSessionKey[QQ_KEY_LENGTH];	// 文件传输密钥
	string	 loginToken;			// 登录令牌

    private:
	QQClient(const QQClient& rhs);		// 不用
	QQClient& operator = (const QQClient& rsh); // 不用

};

#endif // _QQ_CLIENT_H_

